﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using Utilities;
using Utilities.Xna.Framework;
using Utilities.Xna.Framework.Graphics;

using SceneManager.Processors;

namespace SceneManager
{
    /// <summary>
    /// Provides objects and methods to easily display complex scenes with custom effects.
    /// </summary>
    public class SceneManager : DrawableGameComponent
    {
        /// <summary>
        /// Gets a list of all visible objects in the scene
        /// </summary>
        public List<DrawableObject> Objects { get; private set; }

        public bool EnableLighting { get; set; }
        public List<Light> Lights { get; private set; }
        public Color AmbientLight { get; set; }

        /// <summary>
        /// Get the active camera for this scene
        /// </summary>
        public Camera Camera { get; private set; }

        /// <summary>
        /// Gets the width of the current BackBuffer
        /// </summary>
        public int Width
        {
            get
            {
                return Game.GraphicsDevice.PresentationParameters.BackBufferWidth;
            }
        }
        /// <summary>
        /// Gets the height of the current BackBuffer
        /// </summary>
        public int Height
        {
            get
            {
                return Game.GraphicsDevice.PresentationParameters.BackBufferHeight;
            }
        }

        /// <summary>
        /// Get or set the current RenderGraph used to render the scene
        /// </summary>
        public RenderGraph RenderGraph { get; set; }

        private SpriteBatch spriteBatch;
        private SpriteFont defaultFont;

        /// <summary>
        /// Get the current framerate of the scene
        /// </summary>
        public int Fps
        {
            get
            {
                return fpsBuffer.Count;
            }
        }
        private Queue<TimeSpan> fpsBuffer = new Queue<TimeSpan>();
        
        /// <summary>
        /// Initializes a new instance of the SceneManager class with default RenderGraph
        /// </summary>
        /// <param name="game">The game which will own the new scene</param>
        public SceneManager(Game game) : base(game)
        {
            Materials.LoadMaterials(game);

            Objects = new List<DrawableObject>();

            EnableLighting = true;
            Lights = new List<Light>();
            AmbientLight = Color.DarkGray;
            Camera = new Camera(game);

            spriteBatch = new SpriteBatch(game.GraphicsDevice);
            defaultFont = Game.Content.Load<SpriteFont>("DefaultFont");

            RenderGraph = new DefaultRenderGraph(Game, Objects, Lights, AmbientLight);
        }

        public override void Update(GameTime gameTime)
        {
            Camera.Update();

            base.Update(gameTime);
        }
        public override void Draw(GameTime gameTime)
        {
            fpsBuffer.Enqueue(gameTime.TotalGameTime);
            while ((gameTime.TotalGameTime - fpsBuffer.Peek()).TotalMilliseconds > 1000)
                fpsBuffer.Dequeue();

            RenderGraph.Process(Camera, gameTime);

#if DEBUG
            spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.Default, RasterizerState.CullCounterClockwise);

            // Sources de lumières
            foreach (PointLight light in Lights.OfType<PointLight>())
            {
                Vector3 point = Game.GraphicsDevice.Viewport.Project(light.Position, Camera.Projection, Camera.View, Matrix.Identity);
                spriteBatch.DrawRectangle(light.Color, (int)point.X - 1, (int)point.Y - 1, 3, 3);
            }
            foreach (SpotLight light in Lights.OfType<SpotLight>())
            {
                Vector2 source = Game.GraphicsDevice.Viewport.Project(light.Position, Camera.Projection, Camera.View, Matrix.Identity).ToVector2();
                Vector2 destination = Game.GraphicsDevice.Viewport.Project(light.Position + Vector3.Normalize(light.Direction) * 200, Camera.Projection, Camera.View, Matrix.Identity).ToVector2();

                spriteBatch.DrawRectangle(light.Color, (int)source.X - 1, (int)source.Y - 1, 3, 3);
                spriteBatch.DrawLine(light.Color, (int)source.X + 1, (int)source.Y + 1, (int)destination.X, (int)destination.Y);
            }

            // Temps d'execution
            int y = 8;
            double processTime = 0;
            foreach (GraphElement<Processor> element in RenderGraph.Elements)
            {
                spriteBatch.DrawString(defaultFont, element.Name + ": " + Math.Round(element.Value.ProcessTime), new Vector2(8, y), Color.LightGray);
                processTime += element.Value.ProcessTime;
                y += 20;
            }
            spriteBatch.DrawString(defaultFont, "Total: " + Math.Round(processTime), new Vector2(8, y), Color.White);

            spriteBatch.End();
#endif

            base.Draw(gameTime);
        }
    }
}
